package edu.gmu.atelier.generic;

import edu.gmu.atelier.ComplexElement;
import edu.gmu.atelier.Epsilon;
import edu.gmu.atelier.generic.Vector;

/**
 * Vector defined within some vector space.
 * @author James H. Pope
 */
public class ComplexVector implements Vector
{
    public ComplexElement[] components = null;
    
    
    public ComplexVector(ComplexElement x, ComplexElement y)
    {
        this.components = new ComplexElement[2];
        this.components[0] = x;
        this.components[1] = y;
    }
    
    public ComplexVector(ComplexElement x, ComplexElement y, ComplexElement z)
    {
        this.components = new ComplexElement[3];
        this.components[0] = x;
        this.components[1] = y;
        this.components[2] = z;
    }
    
    public ComplexVector(   ComplexElement c1, ComplexElement c2,
                            ComplexElement c3, ComplexElement c4)
    {
        this.components = new ComplexElement[4];
        this.components[0] = c1;
        this.components[1] = c2;
        this.components[2] = c3;
        this.components[3] = c4;
    }
    
    public ComplexVector(ComplexElement[] comps)
    {
        this.components = comps;
//        // defensive copy
//        this.components = new double[comps.length];
//        for (int i = 0; i < comps.length; i++)
//        {
//            this.components[i] = comps[i];
//        }
    }
    
    //------------------------------------------------------------------------//
    // Interface methods
    //------------------------------------------------------------------------//
    public double getElement(int i)
    {
        // @TODO Not sure this should be in interface
        return this.components[i].getReal();
    }
    
    public int getLength()
    {
        return this.components.length;
    }
    
    public void mult(double scalar)
    {
         for (int i = 0; i < components.length; i++)
         {
            components[i].a *= scalar;
            components[i].b *= scalar; // Note sure that this is correct???
        }
    }
    
    public double dot(Vector v)
    {
        ComplexVector v2 = (ComplexVector)v;
        ComplexElement dot = new ComplexElement(0,0);
        for (int i = 0; i < this.components.length; i++)
        {
            // implicitly v1 is transposed
            // GROSSLY INEFFICIENT, NEED TO SOLVE BETTER
            //dot = dot.add( this.components[i].mult( v2.components[i] ) );
            
            ComplexElement here = this.components[i];
            ComplexElement that = v2.components[i];
            dot.a += (here.a * that.a) - (here.b * that.b);
            dot.b += (here.b * that.a) + (here.a * that.b);
        }
        return dot.getReal();
    }
    
    public void add( Vector v )
    {
        ComplexVector o = (ComplexVector)v;
        for (int i = 0; i < components.length; i++)
        {
            //components[i] = components[i].add(o.components[i]);
            this.components[i].a += o.components[i].a;
            this.components[i].b += o.components[i].b;
        }
    }
    
    public void multAdd(double scalar, Vector v)
    {
        ComplexVector o = (ComplexVector)v;
        for (int i = 0; i < components.length; i++)
        {
            //ComplexNumber sm  = o.components[i].mult(scalar);
            //ComplexNumber add = components[i].add( sm );
            //components[i] = add;
            ComplexElement here = this.components[i];
            ComplexElement that = o.components[i];
            
            here.a += that.a * scalar;
            here.b += that.b * scalar; // Note sure that this is correct???
        }
    }
    
    public ComplexVector copy()
    {
        ComplexElement[] comps = new ComplexElement[this.components.length];
        for (int i = 0; i < comps.length; i++)
        {
            comps[i] = this.components[i];
        }
        return new ComplexVector(comps);
    }
    
    public ComplexVector make()
    {
        ComplexElement[] vs = new ComplexElement[this.components.length];
        for (int i = 0; i < vs.length; i++)
        {
            vs[i] = new ComplexElement(0,0);
        }
        return new ComplexVector(vs);
    }
    
    // Really a factory method, "this" is not used
    public ComplexVector make(int n)
    {
        ComplexElement[] vs = new ComplexElement[n];
        for (int i = 0; i < vs.length; i++)
        {
            vs[i] = new ComplexElement(0,0);
        }
        return new ComplexVector(vs);
    }
    
    //------------------------------------------------------------------------//
    // Class instance methods
    //------------------------------------------------------------------------//
    public boolean similar( Object obj, Epsilon e )
    {
        if(obj == this)
        {
            return true;
        }
        
        if( (obj instanceof ComplexVector) == false )
        {
            return false;
        }
        
        ComplexVector o = (ComplexVector)obj;
        
        if( o.components.length != this.components.length )
        {
            return false;
        }
        
        if( e != null )
        {
            for (int i = 0; i < this.components.length; i++)
            {
                // Need epsilon check here
                //if( this.components[i] != o.components[i] )
                if( e.similar(this.components[i].a, o.components[i].a) == false ||
                    e.similar(this.components[i].b, o.components[i].b) == false )
                {
                    return false;
                }
            }
        }
        else
        {
            for (int i = 0; i < this.components.length; i++)
            {
                // Exact check, no epsilon specified
                if( this.components[i].a != o.components[i].a ||
                    this.components[i].b != o.components[i].b    )
                {
                    return false;
                }
            }
        }
        
        return true;
    }
    
    public boolean equals( Object obj )
    {
        return this.similar(obj, null);
    }
    
    public String toString()
    {
        StringBuilder buf = new StringBuilder("[");
        for (int i = 0; i < this.components.length; i++)
        {
            buf.append( this.components[i].toString() );
            buf.append( "," );
        }
        buf.append("]");
        return buf.toString();
    }
}
